/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * This class provides constants and utility method related to Java bytecode
 * generation.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
public final class ByteCode {

    /** Magic for class files. */
    public static final int FILE_MAGIC = 0xCAFEBABE;

    /** Minor version for Java 1.5. */
    public static final short MINOR_VERSION = 0;

    /** Major version for Java 1.5. */
    public static final short MAJOR_VERSION = 49;

    /** Constant for "public" qualifier. */
    public static final short ACC_PUBLIC = 0x0001;

    /** Constant for "protected" qualifier. */
    public static final short ACC_PROTECTED = 0x0004;

    /** Constant for "final" qualifier. */
    public static final short ACC_FINAL = 0x0010;

    /** Constant for "super" qualifier (modern semantics). */
    public static final short ACC_SUPER = 0x0020;

    /** Constant for "interface" qualifier. */
    public static final short ACC_INTERFACE = 0x0200;

    /** Constant for "abstract" qualifier. */
    public static final short ACC_ABSTRACT = 0x0400;

    /** Constant for "synthetic" qualifier. */
    public static final short ACC_SYNTHETIC = 0x1000;

    /** Constant pool: class entry. */
    public static final byte CONSTANT_CLASS = 7;

    /** Constant pool: method reference entry. */
    public static final byte CONSTANT_METHOD_REF = 10;

    /** Constant pool: string entry. */
    public static final byte CONSTANT_STRING = 8;

    /** Constant pool: method name and type entry. */
    public static final byte CONSTANT_NAME_AND_TYPE = 12;

    /** Constant pool: UTF8 entry. */
    public static final byte CONSTANT_UTF8 = 1;

    /** Instruction: pushes <tt>null</tt>. */
    public static final byte INSTR_ACONST_NULL = (byte) 0x01;

    /** Instruction: loads object from array. */
    public static final byte INSTR_AALOAD = (byte) 0x32;

    /** Instruction: loads object from local 0. */
    public static final byte INSTR_ALOAD_0 = (byte) 0x2A;

    /** Instruction: loads object from local 2. */
    public static final byte INSTR_ALOAD_2 = (byte) 0x2C;

    /** Instruction: loads object from local 3. */
    public static final byte INSTR_ALOAD_3 = (byte) 0x2D;

    /** Instruction: loads integer from local 1. */
    public static final byte INSTR_ILOAD_1 = (byte) 0x1B;

    /** Instruction: returns object value. */
    public static final byte INSTR_ARETURN = (byte) 0xB0;

    /** Instruction: pushes integer constant. */
    public static final byte INSTR_BIPUSH = (byte) 0x10;

    /** Instruction: pushes integer constant 0. */
    public static final byte INSTR_ICONST_0 = (byte) 0x03;

    /** Instruction: invokes special (constructor, class initializer) method. */
    public static final byte INSTR_INVOKESPECIAL = (byte) 0xB7;

    /** Instruction: invokes static method. */
    public static final byte INSTR_INVOKESTATIC = (byte) 0xB8;

    /** Instruction: returns. */
    public static final byte INSTR_RETURN = (byte) 0xB1;

    /** Instruction: jumps according to indexed switch. */
    public static final byte INSTR_TABLESWITCH = (byte) 0xAA;

    /**
     * No instance of this class.
     */
    private ByteCode() {
    } // end empty constructor

    /**
     * Encodes a string using 'modified UTF8' format. <br/>
     * One should use this method if a bare conversion is needed but should
     * rely upon {@link java.io.DataOutput#writeUTF(java.lang.String)}
     * for output.
     * @param s string to convert - should not be <tt>null</tt>
     * @return the passed string encoded in 'modified UTF8' format,
     *         as an array of bytes
     */
    public static byte[] modifiedUTF8(final String s) {
        assert s != null : "null s";
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        final int len = s.length();
        for (int i = 0; i < len; i++) {
            final char ch = s.charAt(i);
            if ((ch >= '\u0001') && (ch <= '\u007F')) {
                baos.write(ch & 0x007F);
            } else if ((ch == '\u0000')
                       || ((ch >= '\u0080') && (ch <= '\u07FF'))) {
                baos.write(0x0080 + 0x0040 + ((ch >> 6) & 0x001F));
                baos.write(0x0080 + (ch & 0x003F));
            } else if ((ch >= '\u0800') && (ch <= '\uFFFF')) {
                baos.write(0x0080 + 0x0040 + 0x0020 + ((ch >> 12) & 0x000F));
                baos.write(0x0080 + ((ch >> 6) & 0x003F));
                baos.write(0x0080 + (ch & 0x003F));
            } else {
                baos.write(0x00ED);
                baos.write(0x0080 + 0x0020 + (((ch >> 16) & 0x001F) - 1));
                baos.write(0x0080 + ((ch >> 10) & 0X003F));
                baos.write(0x00ED);
                baos.write(0x0080 + 0x0020 + 0x0010 + ((ch >> 6) & 0x000F));
                baos.write(0x0080 + (ch & 0x003F));
            } // end if/elsif/else
        } // end for
        final byte[] res = baos.toByteArray();
        try {
            baos.close();
        } catch (final IOException ioe) {
            // should not happen
        } // end try/catch
        return res;
    } // end method 'modifiedUTF8(String)'

} // end class 'ByteCode'
